#include "LogicSystem.hpp"
#include "CSession.hpp"
#include "ChatGrpcClient.hpp"
#include "Defer.hpp"
#include "MysqlMgr.hpp"
#include "RedisMgr.hpp"
#include "UserMgr.hpp"
#include "const.hpp"
#include "data.hpp"
#include <cctype>
#include <chrono>
#include <cstring>
#include <functional>
#include <iostream>
#include <json/json.h>
#include <json/reader.h>
#include <json/value.h>
#include <memory>
#include <mutex>
#include <ostream>
#include <string>
#include <thread>
#include <valarray>
#include <vector>

LogicNode::LogicNode(std::shared_ptr<CSession> session,
                     std::shared_ptr<MessageNode> recvnode)
    : _session(session), _recvnode(recvnode) {}

LogicSystem::LogicSystem() : _b_stop(false) {
  RegisterCallBacks();
  _worker_thread = std::thread(&LogicSystem::DealMsg, this);
}

LogicSystem::~LogicSystem() {
  _b_stop = false;
  _consume.notify_one();
  _worker_thread.join();
}

void LogicSystem::PostMsgToQue(std::shared_ptr<LogicNode> msg) {
  std::unique_lock<std::mutex> lock(_mutex);
  _msg_que.push(msg);
  // 由0变为1则发送通知信号
  if (_msg_que.size() == 1) {
    lock.unlock();
    _consume.notify_one();
  }
}

void LogicSystem::DealMsg() {
  while (true) {
    std::unique_lock<std::mutex> lock(_mutex);
    // 未停止情况下，队列为空，等待唤醒
    _consume.wait(lock, [this]() { return !_msg_que.empty() || _b_stop; });
    if (_b_stop) {
      // 关闭状态
      while (!_msg_que.empty()) {
        auto msg_node = _msg_que.front();
        std::cout << "receive message id is " << msg_node->_recvnode->_msg_id
                  << std::endl;
        auto call_back_iter = _fun_callbacks.find(msg_node->_recvnode->_msg_id);
        if (call_back_iter != _fun_callbacks.end()) {
          call_back_iter->second(msg_node);
        }
        _msg_que.pop();
      }
      break;
    }
    // 如果没有停服，且说明队列中有数据
    auto msg_node = _msg_que.front();
    std::cout << "receive message id is " << msg_node->_recvnode->_msg_id
              << std::endl;
    auto call_back_iter = _fun_callbacks.find(msg_node->_recvnode->_msg_id);
    if (call_back_iter != _fun_callbacks.end()) {
      call_back_iter->second(msg_node);
    }
    _msg_que.pop();
  }
}

void LogicSystem::RegisterCallBacks() {
  // 登录请求
  _fun_callbacks[MSG_CHAT_LOGIN] =
      std::bind(&LogicSystem::LoginHandler, this, std::placeholders::_1);
  // 搜索好友
  _fun_callbacks[ID_SEARCH_USER_REQ] =
      std::bind(&LogicSystem::UserSearchHandler, this, std::placeholders::_1);
  // 添加好友申请
  _fun_callbacks[ID_ADD_FRIEND_REQ] =
      std::bind(&LogicSystem::AddFriendHandler, this, std::placeholders::_1);
  // 同意添加好友请求
  _fun_callbacks[ID_AUTH_FRIEND_REQ] =
      std::bind(&LogicSystem::AgreeFriendHandler, this, std::placeholders::_1);
  // 收到消息
  _fun_callbacks[ID_TEXT_CHAT_MSG_REQ] =
      std::bind(&LogicSystem::SendMessageHandler, this, std::placeholders::_1);
  // 用户收到消息确认回包
  _fun_callbacks[ID_TEXT_CHAT_MSG_RSP] =
      std::bind(&LogicSystem::SureMessageHandler, this, std::placeholders::_1);
}

void LogicSystem::LoginHandler(std::shared_ptr<LogicNode> msg_node) {
  Json::Reader reader;
  Json::Value root;
  reader.parse(msg_node->_recvnode->_data, root);
  auto uid = root["uid"].asInt();
  msg_node->_session->SetUserId(uid);
  auto token = root["token"].asString();
  std::cout << "user login uid is " << uid << ", user token is " << token << "."
            << std::endl;

  Json::Value rtvalue;
  Defer defer([&rtvalue, msg_node]() {
    std::string return_str = rtvalue.toStyledString();
    msg_node->_session->Send(return_str.c_str(), return_str.length(),
                             MSG_CHAT_LOGIN_RSP);
  });
  // 从redis中获取用户token是否正确
  std::string uid_str = std::to_string(uid);
  std::string token_key = USER_TOKEN_PREFIX + uid_str;
  std::string token_value = "";
  bool success = RedisMgr::GetInstance().Get(token_key, token_value);
  if (!success) {
    rtvalue["error"] = ErrorCodes::UuidInvalid;
    // 断开该会话
    msg_node->_session->Close();
    return;
  }
  // 登录token与Redis中token不一致
  if (token_value != token) {
    rtvalue["error"] = ErrorCodes::TokenInvalid;
    // 断开该会话
    msg_node->_session->Close();
    return;
  }
  rtvalue["error"] = ErrorCodes::Success;

  // 从Redis中查询用户信息
  std::string base_key = USER_BASE_INFO + uid_str;
  std::shared_ptr<UserInfo> userInfo = std::make_shared<UserInfo>();
  success = GetBaseInfo(msg_node->_session, base_key, uid, userInfo);
  if (!success) {
    rtvalue["error"] = ErrorCodes::UuidInvalid;
    return;
  }
  // 返回数据
  rtvalue["uid"] = uid;
  rtvalue["token"] = token;
  rtvalue["name"] = userInfo->name;
  rtvalue["pwd"] = userInfo->pwd;
  rtvalue["email"] = userInfo->email;

  // 从数据库获取申请列表
  std::vector<std::shared_ptr<FriendApplyInfo>> apply_list;
  if (GetFriendApplyInfo(uid, apply_list)) {
    for (auto item : apply_list) {
      Json::Value obj;
      obj["name"] = item->name;
      obj["email"] = item->email;
      obj["desc"] = item->desc;
      obj["from_uid"] = item->from_uid;
      obj["status"] = item->status;
      obj["sex"] = item->sex;
      rtvalue["apply_list"].append(obj);
    }
  }
  // 获取好友列表
  std::vector<std::shared_ptr<FriendInfo>> friendLists;
  if (GetFriends(uid, friendLists)) {
    for (auto item : friendLists) {
      Json::Value obj;
      obj["name"] = item->name;
      obj["email"] = item->email;
      obj["uid"] = item->uid;
      obj["remark"] = item->remark;
      obj["icon"] = "";
      obj["desc"] = "";
      obj["sex"] = 0;
      rtvalue["friends"].append(obj);
    }
  }
  // 获取消息列表
  std::vector<std::shared_ptr<TextMessage>> messageList;
  MysqlMgr::GetInstance().LoadMessage(uid, messageList);
  if (!messageList.empty()) {
    for (auto textMessage : messageList) {
      Json::Value msg;
      msg["fromuid"] = textMessage->fromuid;
      msg["touid"] = textMessage->touid;
      msg["msg_id"] = textMessage->msg_id;
      msg["send_time"] = textMessage->send_time;
      msg["message"] = textMessage->message;
      rtvalue["message_list"].append(msg);
    }
  }

  // 更新服务器在线人数
  std::string serverName = config.get<std::string>("SelfServer.Name");
  auto res = RedisMgr::GetInstance().HGet(LOGIN_COUNT, serverName);
  int count = 0;
  if (!res.empty()) {
    count = std::stoi(res);
  }
  count++;
  auto count_str = std::to_string(count);
  RedisMgr::GetInstance().HSet(LOGIN_COUNT, serverName, count_str);
  // session 绑定用户uid
  msg_node->_session->SetUserId(uid);
  // 记录当前用户所在服务器roc服务地址
  std::string ipkey = USER_IP_PREFIX + uid_str;
  std::string host = config.get<std::string>("SelfServer.Host");
  std::string rpc_port = config.get<std::string>("SelfServer.RPCPort");
  std::string server_address = host + ":" + rpc_port;
  RedisMgr::GetInstance().Set(ipkey, server_address);
  // Id和session绑定
  UserMgr::GetInstance().SetUserSession(uid, msg_node->_session);
}

void LogicSystem::UserSearchHandler(std::shared_ptr<LogicNode> msg_node) {
  Json::Reader reader;
  Json::Value root;
  // 解析数据
  reader.parse(msg_node->_recvnode->_data, root);
  std::string uid_str = root["uid"].asString();
  std::cout << "User search uid is " << uid_str << std::endl;

  Json::Value rtvalue;
  rtvalue["error"] = ErrorCodes::Success;
  Defer defer([&rtvalue, msg_node]() {
    std::string return_str = rtvalue.toStyledString();
    // 发送数据
    msg_node->_session->Send(return_str.c_str(), return_str.length(),
                             ID_SEARCH_USER_RSP);
  });
  bool b_digit = isPureDigit(uid_str);
  if (b_digit) {
    // 纯数字 通过uid查询
    GetUserByUid(uid_str, rtvalue);
  } else {
    // 通过昵称查询
    GetUserName(uid_str, rtvalue);
  }
}

void LogicSystem::AgreeFriendHandler(std::shared_ptr<LogicNode> msg_node) {
  Json::Reader reader;
  Json::Value root;
  // 解析数据
  reader.parse(msg_node->_recvnode->_data, root);
  auto uid = root["fromuid"].asInt();
  int touid = root["touid"].asInt();
  std::string remark = root["remark"].asString();
  std::cout << "from " << uid << " auth friend to " << touid << ". remark is "
            << remark << std::endl;
  // 返回请求发送者(确认添加好友)的消息用于将好友添加至联系人列表
  Json::Value rtvalue;
  rtvalue["error"] = ErrorCodes::Success;
  Defer defer([&rtvalue, msg_node]() {
    std::string return_str = rtvalue.toStyledString();
    // 发送数据
    msg_node->_session->Send(return_str.c_str(), return_str.length(),
                             ID_AUTH_FRIEND_RSP);
  });

  auto friendInfo = std::make_shared<UserInfo>();
  // 获取对方基本信息
  std::string base_key = USER_BASE_INFO + std::to_string(uid);
  bool is_ok = GetBaseInfo(msg_node->_session, base_key, touid, friendInfo);
  if (!is_ok) {
    rtvalue["error"] = ErrorCodes::UuidInvalid;
    return;
  }
  rtvalue["name"] = friendInfo->name;
  rtvalue["email"] = friendInfo->email;
  rtvalue["uid"] = friendInfo->uid;

  // 更新数据库 接收好友申请 互相将对方设置好友
  bool success = MysqlMgr::GetInstance().MakeFriend(uid, touid, remark);
  if (!success) {
    rtvalue["error"] = ErrorCodes::DataBaseError;
    return;
  }
  // 通知对方
  std::string peer_rpcAddress = "";
  base_key = USER_IP_PREFIX + std::to_string(touid);
  if (!RedisMgr::GetInstance().Get(base_key, peer_rpcAddress)) {
    std::cout << touid << " 用户不在线!";
    return;
  }
  // 获取发送认证请求的用户信息
  auto selfInfo = std::make_shared<UserInfo>();
  base_key = USER_BASE_INFO + std::to_string(touid);
  is_ok = GetBaseInfo(msg_node->_session, base_key, uid, selfInfo);
  if (!is_ok || !selfInfo) {
    std::cout
        << "Error in obtaining authentication request sender information!\n";
    rtvalue["error"] = ErrorCodes::UuidInvalid;
    return;
  }
  // 本服务器rcp地址
  std::string rpc_host = config.get<std::string>("SelfServer.Host");
  std::string rpc_port = config.get<std::string>("SelfServer.RPCPort");
  std::string self_rpcAddress = rpc_host + ":" + rpc_port;
  if (self_rpcAddress == peer_rpcAddress) {
    // 同一个服务器
    auto session = UserMgr::GetInstance().GetSession(uid);
    Json::Value root;
    root["error"] = ErrorCodes::Success;
    root["name"] = selfInfo->name;
    root["uid"] = selfInfo->uid;
    root["email"] = selfInfo->email;
    // 用户备注 从用户端获取 或应用启动时加载
    std::string rt_str = root.toStyledString();
    session->Send(rt_str.c_str(), rt_str.length(), ID_NOTIFY_AUTH_FRIEND_REQ);
  } else {
    AuthFriendReq req;
    req.set_fromuid(uid);
    req.set_touid(touid);
    // 发送请求
    auto rsp =
        ChatGrpcClient::GetInstance().NotifyAuthFriend(peer_rpcAddress, req);
    if (rsp.error() != 0) {
      std::cout << "AgreeFriendHandler: rpc error!\n";
      return;
    }
  }
}

void LogicSystem::SendMessageHandler(std::shared_ptr<LogicNode> msg_node) {
  Json::Reader reader;
  Json::Value root;
  // 解析数据
  reader.parse(msg_node->_recvnode->_data, root);
  auto fromuid = root["fromuid"].asInt();
  int touid = root["touid"].asInt();
  std::string message = root["message"].asString();
  std::cout << "from " << fromuid << "  to " << touid << ". message is "
            << message << std::endl;
  std::string msgid = root["msgid"].asString();
  // 获取消息接收时间
  auto now = std::chrono::system_clock::now();
  // 将时间点转换为时间_t 类型
  std::time_t currentTime = std::chrono::system_clock::to_time_t(now);
  // 将时间_t 类型转换为本地时间
  std::tm *localTime = std::localtime(&currentTime);
  // 定义时间格式
  char timeBuffer[80];
  // 格式化时间
  std::strftime(timeBuffer, sizeof(timeBuffer), "%Y-%m-%d %H:%M:%S", localTime);
  // 保存信息到数据库
  std::shared_ptr<TextMessage> textMessage = std::make_shared<TextMessage>();
  textMessage->msg_id = msgid;
  textMessage->fromuid = fromuid;
  textMessage->touid = touid;
  textMessage->message = message;
  textMessage->send_time = std::string(timeBuffer);
  // 存储消息
  MysqlMgr::GetInstance().SaveMessage(textMessage);
  // 消息转发
  std::string key = USER_IP_PREFIX + std::to_string(touid);
  std::string peer_rpcAddress = "";
  RedisMgr::GetInstance().Get(key, peer_rpcAddress);
  if (peer_rpcAddress.empty()) {
    // 用户不在线
    return;
  }
  // 本服务器rcp地址
  std::string rpc_host = config.get<std::string>("SelfServer.Host");
  std::string rpc_port = config.get<std::string>("SelfServer.RPCPort");
  std::string self_rpcAddress = rpc_host + ":" + rpc_port;
  if (self_rpcAddress == peer_rpcAddress) {
    // 同一个服务器
    auto session = UserMgr::GetInstance().GetSession(touid);
    Json::Value root;
    root["fromuid"] = fromuid;
    root["touid"] = touid;
    root["msg_id"] = msgid;
    root["message"] = message;
    root["send_time"] = textMessage->send_time;
    // 发送消息
    std::string rt_str = root.toStyledString();
    session->Send(rt_str.c_str(), rt_str.length(), ID_TEXT_CHAT_MSG_REQ);
  } else {
    TextChatMsgReq req;
    req.set_fromuid(fromuid);
    req.set_touid(touid);
    req.set_msgid(msgid);
    req.set_msgcontent(message);
    req.set_send_time(textMessage->send_time);
    // 发送请求
    auto rsp =
        ChatGrpcClient::GetInstance().NotifyTextChatMsg(peer_rpcAddress, req);
    if (rsp.error() != 0) {
      std::cout << "SendMessageHandler: rpc error!\n";
      return;
    }
  }
}

void LogicSystem::SureMessageHandler(std::shared_ptr<LogicNode> msg_node) {
  Json::Reader reader;
  Json::Value root;
  // 解析数据
  reader.parse(msg_node->_recvnode->_data, root);
  int touid = root["touid"].asInt();
  if (!root.isMember("msg_ids")) {
    return;
  }
  int msg_id_count = root["msg_ids"].size();
  if (msg_id_count == 0)
    return;
  std::vector<std::string> msg_id_list;
  for (int i = 0; i < msg_id_count; i++) {
    msg_id_list.push_back(root["msg_ids"][i].asString());
  }
  // 存储消息
  MysqlMgr::GetInstance().SureMessage(touid, msg_id_list);
  // 消息转发
  std::string key = USER_IP_PREFIX + std::to_string(touid);
  std::string peer_rpcAddress = "";
  RedisMgr::GetInstance().Get(key, peer_rpcAddress);
  if (peer_rpcAddress.empty()) {
    // 用户不在线
    return;
  }
}

bool LogicSystem::GetBaseInfo(std::shared_ptr<CSession> session,
                              std::string base_key, int uid,
                              std::shared_ptr<UserInfo> userInfo) {
  // 从Redis中查询用户信息
  std::string info_str = "";
  bool success = RedisMgr::GetInstance().Get(base_key, info_str);
  if (success) {
    Json::Reader reader;
    Json::Value root;
    reader.parse(info_str, root);
    userInfo->uid = root["uid"].asInt();
    userInfo->name = root["name"].asString();
    userInfo->pwd = root["pwd"].asString();
    userInfo->email = root["email"].asString();
  } else {
    // redis中没有数据，从mysql中查询
    std::shared_ptr<UserInfo> user_info = nullptr;
    user_info = MysqlMgr::GetInstance().GetUser(uid);
    if (user_info == nullptr) {
      return false;
    }
    userInfo = user_info;
    // 将数据写入Redis中
    Json::Value redis_root;
    redis_root["uid"] = userInfo->uid;
    redis_root["name"] = userInfo->name;
    redis_root["pwd"] = userInfo->pwd;
    redis_root["email"] = userInfo->email;
    std::string root_str = redis_root.toStyledString();
    RedisMgr::GetInstance().Set(base_key, root_str);
  }
  return true;
}

bool LogicSystem::GetFriendApplyInfo(
    int to_uid, std::vector<std::shared_ptr<FriendApplyInfo>> &list) {
  if (!MysqlMgr::GetInstance().GetApplyInfo(to_uid, list, 0, 10)) {
    return false;
  }
  return true;
}

bool LogicSystem::isPureDigit(std::string str) {
  for (char c : str) {
    if (!std::isdigit(c)) {
      return false;
    }
  }
  return true;
}

void LogicSystem::GetUserByUid(std::string uid, Json::Value &rtvalue) {
  // 从Redis中查询用户信息
  std::string base_key = USER_BASE_INFO + uid;
  // 从Redis中查询用户信息
  std::string info_str = "";
  bool success = RedisMgr::GetInstance().Get(base_key, info_str);
  if (success) {
    Json::Reader reader;
    Json::Value root;
    reader.parse(info_str, root);
    rtvalue["uid"] = root["uid"].asInt();
    rtvalue["name"] = root["name"].asString();
    rtvalue["pwd"] = root["pwd"].asString();
    rtvalue["email"] = root["email"].asString();
    return;
  } else {
    // redis中没有数据，从mysql中查询
    std::shared_ptr<UserInfo> userInfo = nullptr;
    userInfo = MysqlMgr::GetInstance().GetUser(std::stoi(uid));
    if (userInfo == nullptr) {
      rtvalue["error"] = ErrorCodes::UuidInvalid;
      return;
    }
    Json::Value root;
    root["uid"] = userInfo->uid;
    root["name"] = userInfo->name;
    root["pwd"] = userInfo->pwd;
    root["email"] = userInfo->email;
    // 将数据写入Redis中
    std::string root_str = root.toStyledString();
    RedisMgr::GetInstance().Set(base_key, root_str);
    // 返回数据
    rtvalue["uid"] = userInfo->uid;
    rtvalue["name"] = userInfo->name;
    rtvalue["pwd"] = userInfo->pwd;
    rtvalue["email"] = userInfo->email;
    return;
  }
}

void LogicSystem::GetUserName(std::string name, Json::Value &rtvalue) {
  std::string base_key = NAME_INFO + name;
  // 优先从redis中查询用户消息
  std::string info_str = "";
  if (RedisMgr::GetInstance().Get(base_key, info_str)) {
    Json::Reader reader;
    Json::Value root;
    reader.parse(info_str, root);
    rtvalue["uid"] = root["uid"];
    rtvalue["name"] = root["name"];
    rtvalue["pwd"] = root["pwd"];
    rtvalue["email"] = root["email"];
    return;
  } else {
    // 从mysql数据库中查询用户信息
    std::shared_ptr<UserInfo> userInfo = MysqlMgr::GetInstance().GetUser(name);
    std::cout << "MysqlMgr::GetInstance().GetUser(name)" << std::endl;
    // 不存在该用户
    if (userInfo == nullptr) {
      rtvalue["error"] = ErrorCodes::NameInvalid;
      return;
    }
    Json::Value root;
    root["uid"] = userInfo->uid;
    root["name"] = userInfo->name;
    root["pwd"] = userInfo->pwd;
    root["email"] = userInfo->email;
    // 将该用户信息写入Redis中
    std::string root_str = root.toStyledString();
    RedisMgr::GetInstance().Set(base_key, root_str);
    // 返回数据
    rtvalue["uid"] = userInfo->uid;
    rtvalue["name"] = userInfo->name;
    rtvalue["pwd"] = userInfo->pwd;
    rtvalue["email"] = userInfo->email;
    return;
  }
}

void LogicSystem::AddFriendHandler(std::shared_ptr<LogicNode> msg_node) {
  Json::Reader reader;
  Json::Value root;
  // 解析数据
  reader.parse(msg_node->_recvnode->_data, root);
  int uid = root["uid"].asInt();
  std::string desc = root["apply"].asString();
  std::string remark = root["remark"].asString();
  int toUid = root["touid"].asInt();
  std::cout << "user id is " << uid << ". Add user's uid is " << toUid
            << ". Friend apply is " << desc << ". Friend remark is " << remark
            << std::endl;

  Json::Value rtvalue;
  rtvalue["error"] = ErrorCodes::Success;
  Defer defer([&rtvalue, msg_node]() {
    std::string return_str = rtvalue.toStyledString();
    // 发送数据
    msg_node->_session->Send(return_str.c_str(), return_str.length(),
                             ID_ADD_FRIEND_RSP);
  });
  FriendApplyInfo friendApplyInfo;
  friendApplyInfo.from_uid = uid;
  friendApplyInfo.to_uid = toUid;
  friendApplyInfo.desc = desc;
  friendApplyInfo.status = 0;
  friendApplyInfo.remark = remark;
  // 先更新数据库
  bool is_ok = MysqlMgr::GetInstance().AddFriendApply(friendApplyInfo);
  if (is_ok) {
    std::cout << "添加好友申请记录到MySQL成功!" << std::endl;
  }
  // 从redis中查找touid对应的server ip
  std::string to_uid_str = std::to_string(toUid);
  std::string to_ip_key = USER_IP_PREFIX + to_uid_str;
  std::string to_server_rpc_address = "";
  if (!RedisMgr::GetInstance().Get(to_ip_key, to_server_rpc_address)) {
    // 对方不在线
    std::cout << "好友不在线！" << std::endl;
    return;
  }
  std::cout << "好友服务器地址: " << to_server_rpc_address << std::endl;
  std::string self_host = config.get<std::string>("SelfServer.Host");
  std::string self_rpc_port = config.get<std::string>("SelfServer.RPCPort");
  std::string self_rpc_address = self_host + ":" + self_rpc_port;
  if (to_server_rpc_address == self_rpc_address) {
    // 同一个服务器
    auto session = UserMgr::GetInstance().GetSession(toUid);
    if (session) {
      Json::Value notify;
      notify["error"] = ErrorCodes::Success;
      notify["applyuid"] = uid;
      notify["name"] = remark;
      notify["desc"] = desc;
      std::string return_str = notify.toStyledString();
      session->Send(return_str.c_str(), return_str.length(),
                    ID_NOTIFY_ADD_FRIEND_REQ);
    }
    return;
  }
  // 在其他服务器
  // 获取自己的基础信息
  std::string base_key = USER_BASE_INFO + std::to_string(uid);
  auto applyInfo = std::make_shared<UserInfo>();
  bool b_info = GetBaseInfo(msg_node->_session, base_key, uid, applyInfo);
  // 添加好友请求
  AddFriendReq addReq;
  addReq.set_touid(toUid);
  addReq.set_applyuid(uid);
  addReq.set_name(applyInfo->name);
  addReq.set_desc(desc);
  // 获取对方服务器rpc服务监听端口
  AddFriendRsp rsp = ChatGrpcClient::GetInstance().NotifyAddFriend(
      to_server_rpc_address, addReq);
  if (rsp.error() != 0) {
    std::cout << "AddFriendHandler: rpc error!" << std::endl;
    return;
  }
}

bool LogicSystem::GetFriends(
    int uid, std::vector<std::shared_ptr<FriendInfo>> &friendList) {
  MysqlMgr::GetInstance().GetFriends(uid, friendList);

  return true;
}